main: Also automatically remount rw /sysroot for `ostree pull` etc.
authorColin Walters <walters@verbum.org>
Thu, 12 Mar 2020 21:10:29 +0000 (21:10 +0000)
committerColin Walters <walters@verbum.org>
Fri, 13 Mar 2020 20:53:26 +0000 (20:53 +0000)
See https://github.com/coreos/fedora-coreos-tracker/issues/343
When we added the read-only sysroot support it broke using "raw"
`ostree pull` and `ostree refs --create` and all of the core repo
CLIs that just operate on a repo and not a sysroot.

Fixing this is a bit ugly as it "layer crosses" things even more.
Extract a helper function that works in both cases.

src/ostree/ot-main.c

index a044cef23fccdc943d3ca933c8d2b1c6754713e9..bffa40c4d44f6066b4d1c25bd07015ee3febfe2f 100644 (file)
@@ -28,6 +28,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/statvfs.h>
+#include <sys/mount.h>
+#include <linux/fs.h>
 
 #include "ot-main.h"
 #include "ostree.h"
@@ -100,6 +102,49 @@ ostree_usage (OstreeCommand *commands,
   return (is_error ? 1 : 0);
 }
 
+/* If we're running as root, booted into an OSTree system and have a read-only
+ * /sysroot, then assume we may need write access.  Create a new mount namespace
+ * if so, and return *out_ns = TRUE.  Otherwise, *out_ns = FALSE.
+ */
+static gboolean
+maybe_setup_mount_namespace (gboolean    *out_ns,
+                             GError     **error)
+{
+  *out_ns = FALSE;
+
+  /* If we're not root, then we almost certainly can't be remounting anything */
+  if (getuid () != 0)
+    return TRUE;
+
+  /* If the system isn't booted via libostree, also nothing to do */
+  if (!glnx_fstatat_allow_noent (AT_FDCWD, "/run/ostree-booted", NULL, 0, error))
+    return FALSE;
+  if (errno == ENOENT)
+    return TRUE;
+
+  glnx_autofd int sysroot_subdir_fd = glnx_opendirat_with_errno (AT_FDCWD, "/sysroot", TRUE);
+  if (sysroot_subdir_fd < 0)
+    {
+      if (errno != ENOENT)
+        return glnx_throw_errno_prefix (error, "opendirat");
+      /* No /sysroot - nothing to do */
+      return TRUE;
+    }
+
+  struct statvfs stvfs;
+  if (fstatvfs (sysroot_subdir_fd, &stvfs) < 0)
+    return glnx_throw_errno_prefix (error, "fstatvfs");
+  if (stvfs.f_flag & ST_RDONLY)
+    {
+      if (unshare (CLONE_NEWNS) < 0)
+        return glnx_throw_errno_prefix (error, "preparing writable sysroot: unshare (CLONE_NEWNS)");
+
+      *out_ns = TRUE;
+    }
+
+  return TRUE;
+}
+
 static void
 message_handler (const gchar *log_domain,
                  GLogLevelFlags log_level,
@@ -220,6 +265,19 @@ parse_repo_option (GOptionContext *context,
 {
   g_autoptr(OstreeRepo) repo = NULL;
 
+  /* This is a bit of a brutal hack; we set up a mount
+   * namespace if it appears that we may need it.  It'd
+   * be better to do this more precisely in the future.
+   */
+  gboolean setup_ns = FALSE;
+  if (!maybe_setup_mount_namespace (&setup_ns, error))
+    return FALSE;
+  if (setup_ns)
+    {
+      if (mount ("/sysroot", "/sysroot", NULL, MS_REMOUNT | MS_SILENT, NULL) < 0)
+        return glnx_null_throw_errno_prefix (error, "Remounting /sysroot read-write");
+    }
+
   if (repo_path == NULL)
     {
       g_autoptr(GError) local_error = NULL;
@@ -452,27 +510,11 @@ ostree_admin_option_context_parse (GOptionContext *context,
        */
       if (ostree_sysroot_is_booted (sysroot))
         {
-          int sysroot_fd = ostree_sysroot_get_fd (sysroot);
-          g_assert_cmpint (sysroot_fd, !=, -1);
-
-          glnx_autofd int sysroot_subdir_fd = glnx_opendirat_with_errno (sysroot_fd, "sysroot", TRUE);
-          if (sysroot_subdir_fd < 0)
-            {
-              if (errno != ENOENT)
-                return glnx_throw_errno_prefix (error, "opendirat");
-            }
-          else if (getuid () == 0)
-            {
-              struct statvfs stvfs;
-              if (fstatvfs (sysroot_subdir_fd, &stvfs) < 0)
-                return glnx_throw_errno_prefix (error, "fstatvfs");
-              if (stvfs.f_flag & ST_RDONLY)
-                {
-                  if (unshare (CLONE_NEWNS) < 0)
-                    return glnx_throw_errno_prefix (error, "preparing writable sysroot: unshare (CLONE_NEWNS)");
-                  ostree_sysroot_set_mount_namespace_in_use (sysroot);
-                }
-            }
+          gboolean setup_ns = FALSE;
+          if (!maybe_setup_mount_namespace (&setup_ns, error))
+            return FALSE;
+          if (setup_ns)
+            ostree_sysroot_set_mount_namespace_in_use (sysroot);
         }
 
       /* Released when sysroot is finalized, or on process exit */